package netcode.client;

import sw.EngAdroid.*;

import java.io.IOException;
import java.io.Serializable;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentSkipListMap;

import netcode.helper.ClientMessageListener;
import netcode.helper.ErrorMessage;
import netcode.helper.Message;
import netcode.helper.NetcodeHelper;
import netcode.helper.PacketResponse;
//import netcode.server.ConnectedClient;
//import netcode.server.ConnectedClientFactory;

/**
 * This class represents the client network code for the SwEng II 2012 project
 * and UW-Parkside. It uses UDP for message transmission.
 * 
 * @author Anthony Dare
 */
public class Client extends Thread {
	private static final boolean VERBOSE = true;
	private DatagramSocket clientSocket;
	private InetSocketAddress serverSocketAddress;
	private int clientKey;
	private String clientName;
	private boolean connected;
	private ClientMessageListener listener;
	private Game game;
	private Map<Byte, PacketResponse> packetResponses;
	private Lobby lobby;
	private Queue<ErrorMessage> errorQueue;
	private boolean running;

	/**
	 * This method is purely for testing and demonstration purposes.
	 * 
	 * @param args
	 */
	public static void main(String args[]) {
		try {
			if (VERBOSE) {
				System.out.println("Main: Instantiating Client...");
			}
			Game game = new Game();
			Client client = new Client(NetcodeHelper.SERVER_ADDRESS,
					NetcodeHelper.SERVER_PORT, "test", game);
			if (VERBOSE) {
				System.out.println("Main: Client Instantiated.");
			}

			if (VERBOSE) {
				System.out.println("Main: Starting Client Thread...");
			}
			client.start();
			if (VERBOSE) {
				System.out.println("Main: Client Thread Started.");
			}
			client.sendConnectionRequest();
			if (VERBOSE) {
				System.out.println("Main: Waiting for Client Thread to end...");
			}
			try {
				client.join();
				if (VERBOSE) {
					System.out.println("Main: Client Thread Ended.");
				}
			} catch (Exception e) {
				System.err.println(e);
				if (VERBOSE) {
					System.out
							.println("Main: Problem occurred ending client thread.");
				}
			}
		} catch (Exception e) {
			System.err.println(e);
		}
	}

	public Client(String hostname, int port, String clientName, Game game)
			throws Exception {
		super();
		clientSocket = new DatagramSocket();
		serverSocketAddress = new InetSocketAddress(hostname, port);
		this.clientName = clientName;
		connected = false;
		this.game = game;
		packetResponses = new ConcurrentSkipListMap<Byte, PacketResponse>();
		lobby = new Lobby();
		clientSocket.connect(serverSocketAddress);
	}

	@Override
	public void run() {
		running = true;
		while (running) {
			try {
				byte receivedBuffer[] = new byte[256];
				DatagramPacket receivedPacket = new DatagramPacket(
						receivedBuffer, receivedBuffer.length);
				if (VERBOSE) {
					System.out.println("Waiting to receive packet...");
				}
				clientSocket.receive(receivedPacket);
				if (VERBOSE) {
					System.out.println("Packet Received.");
				}
				if (receivedPacket.getData()[NetcodeHelper.BB_MESSAGE_ID_INDEX] == NetcodeHelper.BRAINS_AND_BETS
						&& receivedPacket.getData()[NetcodeHelper.BB_MESSAGE_VERSION_INDEX] == NetcodeHelper.MESSAGE_VERSION) {
					/*
					 * Implement the catching of packets here. Just add a new
					 * case, with the appropriate NetcodeHelper code, and call
					 * the appropriate handler method, passing in whatever info
					 * is needed.
					 */
					switch (receivedPacket.getData()[NetcodeHelper.BB_MESSAGE_TYPE_INDEX]) {
					case NetcodeHelper.CONNECTION_RESPONSE:
						handleConnectionResponse(receivedPacket);
						break;
					case NetcodeHelper.SERVER_LOBBY_INFO_RESPONSE:
						handleServerLobbyInfoResponse(receivedPacket);
						break;
					case NetcodeHelper.GAME_LOBBY_INFO:
						handleGameLobbyInfo(receivedPacket);
						break;
					case NetcodeHelper.QUESTION:
						handleQuestion(receivedPacket);
						break;
					case NetcodeHelper.BETTING_ODDS:
						handleBettingOdds(receivedPacket);
						break;
					case NetcodeHelper.ROUND_RESULTS:
						handleRoundResults(receivedPacket);
						break;
					case NetcodeHelper.GAME_RESULTS:
						handleGameResults(receivedPacket);
						break;
					case NetcodeHelper.ARE_YOU_THERE:
						handleAreYouThere(receivedPacket);
						break;
					case NetcodeHelper.MESSAGE_RECEIVED:
						handleMessageReceived(receivedPacket);
					case NetcodeHelper.ERROR:
						handleError(receivedPacket);
						break;
					}
					if (listener != null) {
						listener.onMessageReceived(receivedPacket.getData()[NetcodeHelper.BB_MESSAGE_TYPE_INDEX]);
					}
					/*
					 * End of packet catching.
					 */
				}

			} catch (Exception e) {
				System.err.println(e);
			}
		}
	}

	public void setListener(ClientMessageListener listener) {
		this.listener = listener;
	}

	private boolean sendPacketWaitForResponse(DatagramPacket packet) {
		if (VERBOSE) {
			System.out.println("Entered sendPacketWaitForResponse().");
		}
		byte messageType = packet.getData()[NetcodeHelper.BB_MESSAGE_TYPE_INDEX];
		if (VERBOSE) {
			System.out.println("messageType: " + messageType);
		}
		PacketResponse response = new PacketResponse();
		packetResponses.put(messageType, response);
		try {
			int attemptCounter = 0;
			do {
				if (VERBOSE) {
					System.out.println("Sending Packet...");
				}
				clientSocket.send(packet);
				if (VERBOSE) {
					System.out.println("Waiting for response...");
				}
				Thread.sleep(NetcodeHelper.MILLISECONDS_BETWEEN_SENDING_MESSAGE);
				attemptCounter++;
			} while (!response.isReceived()
					&& attemptCounter < NetcodeHelper.NUMBER_OF_SENDING_MESSAGE_ATTEMPTS);
			if (VERBOSE) {
				System.out.println("Response received.");
			}
			if (response.isErrorOccurred()) {
				return false;
			} else {
				packetResponses.remove(messageType);
				return true;
			}
		} catch (Exception e) {
			System.err.println(e);
			return false;
		}

	}

	/**
	 * Handles the CONNECTION_RESPONSE from the server, setting clientKey with
	 * the data provided in the packet. Also sets the connected boolean to true.
	 * This method is complete.
	 * 
	 * @param packet
	 */
	public void handleConnectionResponse(DatagramPacket packet) {
		if (VERBOSE) {
			System.out.println("CONNECTION_RESPONSE received:");
		}
		// Using message object
		Message message = new Message(packet);
		if (message.hasNext()) {
			try {
				clientKey = message.nextInt();
				if (VERBOSE) {
					System.out.println("clientKey: " + clientKey);
				}
			} catch (Exception e) {
				System.err.println(e);
			}
		}
		connected = true;
	}

	/**
	 * Sends the CONNECTION_REQUEST to the server, including the player name in
	 * the packet data. This method is complete.
	 */
	public void sendConnectionRequest() {
		try {
			if (VERBOSE) {
				System.out
						.println("Client: Creating CONNECTION_REQUEST to be sent...");
			}

			// Using the Message object implementation
			System.out.println("1");
			Message message = new Message(NetcodeHelper.CONNECTION_REQUEST);
			System.out.println("1");
			message.add(clientName);
			System.out.println("1");
			message.finalize();
			System.out.println("1");
			DatagramPacket sendPacket = new DatagramPacket(message.getData(),
					message.getDataLength(), serverSocketAddress);
			System.out.println("1");

			if (VERBOSE) {
				System.out.println("Client: Packet Created.");
			}

			if (VERBOSE) {
				System.out.println("Client: Sending packet...");
			}
			do {
				clientSocket.send(sendPacket);
				if (VERBOSE) {
					System.out.println("Client: Packet Sent.");
				}

				if (VERBOSE) {
					System.out.println("Client: Waiting for response...");
				}
				Thread.sleep(NetcodeHelper.MILLISECONDS_BETWEEN_SENDING_MESSAGE);
			} while (!connected);
			if (VERBOSE) {
				System.out.println("Client: Response Received");
			}

			if (VERBOSE) {
				System.out.println("Client: Processing Response...");
			}

			if (VERBOSE) {
				System.out.println("Client: Connected to Server! Client Key: "
						+ clientKey);
			}

		} catch (Exception e) {
			System.err.println(e);
		}
	}

	/**
	 * Sends the JOIN_GAME_REQUEST to the server, including the clientKey and
	 * GameID in the packet data. This method is complete.
	 * 
	 * @param gameID
	 */
	public void sendJoinGameRequest(int gameID) {
		Message message = new Message(NetcodeHelper.JOIN_GAME_REQUEST);
		message.add(clientKey);
		message.add(gameID);
		message.finalize();
		try {
			DatagramPacket sendPacket = new DatagramPacket(message.getData(),
					message.getDataLength(), serverSocketAddress);
			// sendPacketWaitForResponse(sendPacket);
			clientSocket.send(sendPacket);
		} catch (Exception e) {
			System.err.println(e);
		}
	}

	/**
	 * COMPLETE Author: Nicole
	 */
	public void sendServerLobbyInfoRequest() {
		if (VERBOSE) {
			System.out.println("SERVER_LOBBY_INFO_REQUEST creation:");
		}
		Message message = new Message(NetcodeHelper.SERVER_LOBBY_INFO_REQUEST);
		message.add(clientKey);
		if (VERBOSE) {
			System.out.println("clientKey: " + clientKey);
		}
		message.finalize();
		try {
			DatagramPacket sendPacket = new DatagramPacket(message.getData(),
					message.getDataLength(), serverSocketAddress);
			// sendPacketWaitForResponse(sendPacket);
			clientSocket.send(sendPacket);
			if (VERBOSE) {
				System.out.println("SERVER_LOBBY_INFO_REQUEST sent.");
			}
		} catch (Exception e) {
			System.err.println(e);
			if (VERBOSE) {
				System.out.println("Error sending SERVER_LOBBY_INFO_REQUEST.");
			}
		}
	}

	/*
	 * COMPLETE Author: Nicole (Anthony) Sorry Nicole, couldn't get this to
	 * work. Kept getting string index errors.
	 */
	private void handleServerLobbyInfoResponse(DatagramPacket packet) {
		if (VERBOSE) {
			System.out.println("SERVER_LOBBY_INFO_RESPONSE received:");
		}

		// (Anthony) Code Begins
		Message receivedMessage = new Message(packet);
		Lobby newLobby = new Lobby();
		while (receivedMessage.hasNext()) {
			try {
				int gameID = receivedMessage.nextInt();
				if (VERBOSE) {
					System.out.println("gameID: " + gameID);
				}
				String gameName = receivedMessage.nextString();
				if (VERBOSE) {
					System.out.println("gameName: " + gameName);
				}
				int gameState = receivedMessage.nextInt();
				if (VERBOSE) {
					System.out.println("gameState: " + gameState);
				}
				newLobby.insertGame(gameID, gameName, gameState);
				if (VERBOSE) {
					System.out.println("Game added to lobby.");
				}
			} catch (Exception e) {
				System.err.println(e);
			}
			lobby = newLobby;
		}
		// (Anthony) Code Ends

		// // Convert to Message from packet
		// Message lobbyInfoMessage = new Message(packet);
		// // Holds data sent from packet in string form
		// String lobbyInfo;
		// try {
		// // Get the String
		// lobbyInfo = lobbyInfoMessage.nextString();
		// // Parse through the the string for game information
		// while (!lobbyInfo.isEmpty()) {
		// // This ensures that any extra spaces are eliminated
		// int start = lobbyInfo.indexOf("<");
		// // This captures the end of the game information
		// int end = lobbyInfo.indexOf(">");
		//
		// // Extract information
		// extractLobbyInformation(lobbyInfo.substring(start + 1, end));
		//
		// // Remove handled game info
		// lobbyInfo = lobbyInfo.substring(end + 1);
		// }
		//
		// } catch (Exception e) {
		// System.err.println(e);
		// }

	}

	/*
	 * Helper Method for Lobby Info COMPLETE Author Nicole
	 * DEPRICATED
	 */
//	private void extractLobbyInformation(String info) {
//		int end = info.indexOf(";");
//		if (end != -1) {
//			// Get the gameID
//			int gameId = Integer.parseInt(info.substring(0, end));
//
//			// Remove the game id from the String
//			info = info.substring(end + 1);
//
//			end = info.indexOf(";");
//			// Get the game name
//			String gameName = info.substring(0, end);
//
//			// Get players starting without ';' from game name
//			ArrayList<Player> players = new ArrayList<Player>();
//			info = info.substring(0 + 1);
//			while (!info.isEmpty()) {
//				end = info.indexOf(";");
//				// Get player name
//				String pName = info.substring(0, end);
//				// Add to array list of players
//				players.add(new Player(pName));
//				// Remove player information with ;
//				info = info.substring(end + 1);
//			}
//
//			// Add to lobby Games
//			// Insert a new Game with no players
//			lobby.insertGame(gameId, gameName, players);
//
//			// For command line checking
//			if (VERBOSE) {
//				System.out.println("Client Received Game Lobby Info...");
//				System.out.println("Game ID: " + gameId);
//				System.out.println("Game Name:" + gameName);
//				System.out.println("Players:" + gameName);
//				for (int i = 0; i < players.size(); i++) {
//					System.out.println(players.get(i));
//				}
//				System.out.println();
//			}
//		}
//	}

	/*
	 * Handles incoming message in format <GameID;player1;player2...> COMPLETE
	 * 
	 * @author Nicole
	 */
	private void handleGameLobbyInfo(DatagramPacket packet) {
		if (VERBOSE) {
			System.out.println("GAME_LOBBY_INFO received:");
		}
		// Convert to Message from packet
		Message lobbyInfoMessage = new Message(packet);
		try {
			// Get Message Information
			int gameID = lobbyInfoMessage.nextInt();
			if (VERBOSE) {
				System.out.println("  gameID: " + gameID);
			}
			String gameName = lobbyInfoMessage.nextString();
			// Get Players Names
			ArrayList<Player> players = new ArrayList<Player>();
			if (VERBOSE) {
				System.out.println("  Players:");
			}
			while (lobbyInfoMessage.hasNext()) {
				String pName = lobbyInfoMessage.nextString();
				players.add(new Player(pName));
				if (VERBOSE) {
					System.out.println("    " + pName);
				}
			}
			// Set Players of game object
			game.setPlayers(players);

			// (Anthony) added this in to keep track of gameID
			game.setGameID(gameID);
			
			game.setName(gameName);

			// For command line checking
			if (VERBOSE) {
				System.out
						.println("Client Recieved Game Lobby Information...: ");
				System.out.println("Game ID: " + gameID);
				System.out.println("Players: ");
				for (int i = 0; i < players.size(); i++) {
					System.out.println(players.get(i).getPlayer_name());
				}
				System.out.println();
			}
		} catch (Exception e) {
			System.err.println(e);
		}
	}

	/**
	 * 
	 * @param gameName
	 * @param rounds
	 * @param categories
	 *            Format of message:
	 *            <clientkey;clientName;gameName;rounds;category
	 *            [0];categoryLength;category[1];category[2]...> Refactored by
	 *            Nicole
	 * 
	 *            (Anthony) Getting string index errors server side.
	 *            Refactoring.
	 */
	public void sendCreateGameRequest(String gameName, int rounds,
			String[] categories) {
		if (VERBOSE) {
			System.out.println("CREATE_GAME_REQUEST creation:");
		}
		Message message = new Message(NetcodeHelper.CREATE_GAME_REQUEST);

		// (Anthony) Code Begins
		message.add(clientKey);
		if (VERBOSE) {
			System.out.println("clientKey: " + clientKey);
		}
		message.add(gameName);
		if (VERBOSE) {
			System.out.println("gameName: " + gameName);
		}
		message.add(rounds);
		if (VERBOSE) {
			System.out.println("rounds: " + rounds);
		}
		if (categories != null) {
			for (int i = 0; i < categories.length; i++) {
				message.add(categories[i]);
				if (VERBOSE) {
					System.out.println("categories[" + i + "]: "
							+ categories[i]);
				}
			}
		}
		message.finalize();
		try {
			DatagramPacket packet = new DatagramPacket(message.getData(),
					message.getDataLength(), serverSocketAddress);
			clientSocket.send(packet);
			if (VERBOSE) {
				System.out.println("CREATE_GAME_REQUEST sent.");
			}
		} catch (Exception e) {
			System.err.println(e);
			if (VERBOSE) {
				System.out.println("Error sending CREATE_GAME_REQUEST.");
			}
		}
		// (Anthony) Code Ends

		// String info = "<" + clientKey + ";" + gameName + ";" + rounds + ";"
		// + categories.length + ";";
		// if (categories != null) {
		// for (int i = 0; i < categories.length; i++) {
		// info += categories[i] + ";";
		// }
		// }
		// info += ">";
		// message.add(info);
		// message.finalize();
		// try {
		// DatagramPacket sendPacket = new DatagramPacket(message.getData(),
		// message.getDataLength(), serverSocketAddress);
		// clientSocket.send(sendPacket);
		// } catch (Exception e) {
		// System.err.println(e);
		// }
	}

	// Jim C
	public void sendStartGameRequest(int gameID) {
		try {
			if (VERBOSE) {
				System.out
						.println("Client: Creating Start Game Request Packet to be sent...");
			}

			// Using the Message object implementation
			Message message = new Message(NetcodeHelper.START_GAME_REQUEST);
			message.add(clientKey);
			message.add(gameID);
			message.finalize();

			DatagramPacket sendPacket = new DatagramPacket(message.getData(),
					message.getDataLength(), serverSocketAddress);
			clientSocket.send(sendPacket);

			if (VERBOSE) {
				System.out.println("Client: Start Game Request packet sent.");
			}

		} catch (Exception e) {
			System.err.println(e);
		}
	}

	// Jim C
	private void handleQuestion(DatagramPacket packet) {
		if (VERBOSE) {
			System.out.println("QUESTION received:");
		}
		// Using the Message object
		Message receivedMessage = new Message(packet);
		if (receivedMessage.hasNext()) {
			try {
				String question = receivedMessage.nextString();

				game.getQuestion().setText(question);

				if (VERBOSE) {
					System.out.println("Question packet value: " + question);
				}

			} catch (Exception e) {
				System.err.println(e);
			}
		}
	}

	// Jim C
	public void sendAnswer(int gameID, int answer) {
		try {
			if (VERBOSE) {
				System.out
						.println("Client: Creating Send Answer Packet to be sent...");
			}

			// Using the Message object implementation
			Message message = new Message(NetcodeHelper.ANSWER);
			message.add(clientKey);
			message.add(gameID);
			message.add(answer);
			message.finalize();

			DatagramPacket sendPacket = new DatagramPacket(message.getData(),
					message.getDataLength(), serverSocketAddress);
			clientSocket.send(sendPacket);

			if (VERBOSE) {
				System.out.println("Client: Send Answer packet sent.");
			}

		} catch (Exception e) {
			System.err.println(e);
		}
	}

	/**
	 * --- Needs Testing --- Gets the last round's results
	 * 
	 * Jim J
	 * 
	 * @param packet
	 */
	private void handleRoundResults(DatagramPacket packet) {
		if (VERBOSE) {
			System.out.println("ROUND_RESULTS received:");
		}
		Message receivedMessage = new Message(packet);

		// <Player's name, Player's payout>
		Map<String, Integer> playerPayout = new HashMap<String, Integer>();

		try {
			int gID = receivedMessage.nextInt();
			int answer = receivedMessage.nextInt();
			game.getQuestion().setAnswer(answer);
			
			if (VERBOSE)
				System.out
						.println("Client: received round results & got game ID.");
		} catch (Exception e) {
			e.printStackTrace();

			if (VERBOSE)
				System.out
						.println("Client Error: receiving round results & game ID.");
		}

		while (receivedMessage.hasNext()) {
			try {
				playerPayout.put(receivedMessage.nextString(),
						receivedMessage.nextInt());

			} catch (Exception e) {
				e.printStackTrace();

				if (VERBOSE)
					System.out.println("Client Error: receiving round results");
			}
		}
		
		// Translate this over to the game object
		Iterator<String> nameIterator = playerPayout.keySet().iterator();
		while (nameIterator.hasNext()){
			String name = nameIterator.next();
			List<Player> players = game.getPlayers();
			
			for (int i = 0; i < players.size(); i++){
				if(name.equals(players.get(i).getPlayer_name())){
					int currentGameChips = players.get(i).getGame_chips();
					currentGameChips += playerPayout.get(name);
					players.get(i).setGame_chips(currentGameChips);
					
					if (VERBOSE) {
						System.out.println("Client Round Results:");
						System.out.println(" Player: " + name);
						System.out.println(" Payout: " + playerPayout.get(name));
						System.out.println(" Total Chips: " + currentGameChips);
					}
				}
			}
		}
	}

	// Adam P.
	public void sendNextRoundRequest(int gameID) {
		try {
			if (VERBOSE) {
				System.out
						.println("Client: Creating Next Round Request Packet to be sent...");
			}

			// Using the Message object implementation
			Message message = new Message(NetcodeHelper.NEXT_ROUND_REQUEST);
			message.add(clientKey);
			message.add(gameID);
			message.finalize();

			DatagramPacket sendPacket = new DatagramPacket(message.getData(),
					message.getDataLength(), serverSocketAddress);
			clientSocket.send(sendPacket);

			if (VERBOSE) {
				System.out.println("Client: Next Round Request packet sent.");
			}

		} catch (Exception e) {
			System.err.println(e);
		}
	}

	// Adam P.
	public void sendLeaveGameRequest(int gameID) {
		try {
			if (VERBOSE) {
				System.out
						.println("Client: Creating Leave Game Request Packet to be sent...");
			}

			// Using the Message object implementation
			Message message = new Message(NetcodeHelper.LEAVE_GAME_REQUEST);
			message.add(clientKey);
			message.add(gameID);
			message.finalize();

			DatagramPacket sendPacket = new DatagramPacket(message.getData(),
					message.getDataLength(), serverSocketAddress);
			clientSocket.send(sendPacket);

			if (VERBOSE) {
				System.out.println("Client: Leave Game Request packet sent.");
			}

		} catch (Exception e) {
			System.err.println(e);
		}
	}

	/**
	 * by Adam P.
	 * 
	 * @param packet
	 * @return Map of Player's names and the number of chips they won
	 * 			Note: this does not include the number of permanent chips
	 */
	private void handleGameResults(DatagramPacket packet) {
		if (VERBOSE) {
			System.out.println("GAME_RESULTS received:");
		}
		
		Message receivedMessage = new Message(packet);

		// <Player's name, Player's won chips>
		Map<String, Integer> gameResults = new HashMap<String, Integer>();

		try {
			int gID = receivedMessage.nextInt();

			if (VERBOSE)
				System.out
						.println("Client: received game results & got game ID.");
		} catch (Exception e) {
			e.printStackTrace();

			if (VERBOSE)
				System.out
						.println("Client Error: receiving game results & game ID.");
		}

		while (receivedMessage.hasNext()) {
			try {
				gameResults.put(receivedMessage.nextString(),
						receivedMessage.nextInt());

			} catch (Exception e) {
				e.printStackTrace();

				if (VERBOSE)
					System.out.println("Client Error: receiving round results");
			}
		}

		Iterator<String> nameIterator = gameResults.keySet().iterator();
		while (nameIterator.hasNext()) {
			String name = nameIterator.next();
			List<Player> players = game.getPlayers();
			
			for (int i = 0; i < players.size(); i++) {
				if (name.equals(players.get(i).getPlayer_name())) {
					int wonGameChips = gameResults.get(name);
					players.get(i).setGame_chips(wonGameChips);
					
					if (VERBOSE) {
						System.out.println("Client Game Results:");
						System.out.println("Player: " + name);
						System.out.println("Won Chips: " + gameResults.get(name));
					}
				}
			}
		}
	}

	public void sendDisconnect() {

		try {
			if (VERBOSE) {
				System.out.println("Client: Creating Disconnect");
			}

			// Using the Message object implementation
			Message message = new Message(NetcodeHelper.DISCONNECT);
			message.add(clientKey);
			message.finalize();

			DatagramPacket sendPacket = new DatagramPacket(message.getData(),
					message.getDataLength(), serverSocketAddress);
			clientSocket.send(sendPacket);

			if (VERBOSE) {
				System.out.println("Client: Disconect Sent");
			}

		} catch (Exception e) {
			System.err.println(e);
		}

	}

	/**
	 * Gets the betting odds from the server and puts them
	 * in a list.
	 * 
	 * by Jim J
	 * 
	 * @param packet
	 */
	private void handleBettingOdds(DatagramPacket packet) {
		if (VERBOSE) {
			System.out.println("BETTING_ODDS received:");
		}
		Message receivedMessage = new Message(packet);
		List<Guess> guessList = game.getGuesses();
		guessList.clear();
		while (receivedMessage.hasNext()) {
			try {
				// Skipping name for now. Implement in a later version.
				// String name = receivedMessage.nextString();
				// if(VERBOSE){
				// System.out.println("name: " + name);
				// }
				int guess = receivedMessage.nextInt();
				if (VERBOSE) {
					System.out.println("guess: " + guess);
				}
				int odds = receivedMessage.nextInt();
				if (VERBOSE) {
					System.out.println("odds: " + odds);
				}
				
				Guess playerGuess = new Guess(guess, odds);
				if (VERBOSE) {
					System.out.println("Created new Guess");
				}
				guessList.add(playerGuess);
				if (VERBOSE) {
					System.out.println("Added guess to guessList");
				}
			} catch (Exception e) {
				System.err.println(e);
			}
		}
	}

	
	
	
	/**
	 * Sends all of the player's bets to the server
	 * 
	 * by Jim J
	 * 
	 * @param bets
	 *            List of a player's bets
	 */
	public void sendSubmitBets(List<Bet> bets, int gameID) {

		Message sendMessage = new Message(NetcodeHelper.SUBMIT_BETS);
		sendMessage.add(clientKey);
		sendMessage.add(gameID);
		
		if (VERBOSE) {
			System.out.println("SEND SUBMIT BETS START");
			System.out.println(bets);
			System.out.println("end of printing bets");
		}
		
		for (int i = 0; i < bets.size(); i++) {
			sendMessage.add(bets.get(i).getChoice());
			sendMessage.add(bets.get(i).getPermaChips());
			sendMessage.add(bets.get(i).getGameChips());
			if (VERBOSE) {
				System.out.println("Choice: " + bets.get(i).getChoice());
				System.out.println("Perm Chips: " + bets.get(i).getPermaChips());
				System.out.println("Game Chips: " + bets.get(i).getGameChips());
			}
		}

		sendMessage.finalize();

		try {

			DatagramPacket sendPacket = new DatagramPacket(
					sendMessage.getData(), sendMessage.getDataLength(),
					serverSocketAddress);
			clientSocket.send(sendPacket);

		} catch (IOException e) {
			e.printStackTrace();
		}

		if (VERBOSE) {
			System.out.println("Client: Sent Answer packet sent.");
		}
	}

	public void sendAreYouThere() {

		try {
			if (VERBOSE) {
				System.out.println("Client: Creating Ping");
			}

			// Using the Message object implementation
			Message message = new Message(NetcodeHelper.ARE_YOU_THERE);
			message.finalize();

			DatagramPacket sendPacket = new DatagramPacket(message.getData(),
					message.getDataLength(), serverSocketAddress);
			clientSocket.send(sendPacket);

			if (VERBOSE) {
				System.out.println("Client: Pinged");
			}

		} catch (Exception e) {
			System.err.println(e);
		}

	}

	/*
	 * Send Join Game Invite Email message to server
	 * Nicole
	 */
	public void sendJoinGameInviteEmail(String to, String from, String content){
		if (VERBOSE){
			System.out.println("Starting send friend join request message...");
		}
		try {
			Message friendJoinRequestMessage = new Message(NetcodeHelper.JOIN_GAME_INVITE);
			friendJoinRequestMessage.add(clientKey);
			friendJoinRequestMessage.add(game.getName());
			friendJoinRequestMessage.add(to);
			friendJoinRequestMessage.add(from);
			friendJoinRequestMessage.add(content);
			friendJoinRequestMessage.finalize();
			if (VERBOSE) {
				System.out.println("Added Content to Message");
			}
		
			DatagramPacket packet = new DatagramPacket(friendJoinRequestMessage.getData(),
					friendJoinRequestMessage.getDataLength(), serverSocketAddress);
			clientSocket.send(packet);
			if (VERBOSE) {
				System.out.println("JOIN_GAME_INVITE message sent to server .");
			}
		} catch (Exception e) {
			System.err.println(e);
		}
	}
	
	private void handleAreYouThere(DatagramPacket packet) {
		if (VERBOSE) {
			System.out.println("ARE_YOU_THERE received:");
		}
		sendMessageReceived(NetcodeHelper.ARE_YOU_THERE);
	}

	
	
	// Client-side version of the sendMessageReceived method
	private void sendMessageReceived(byte messageType) {
		try {
			if (VERBOSE) {
				System.out.println("Sending MESSAGE_RECEIVED message...");
			}
			Message message = new Message(NetcodeHelper.MESSAGE_RECEIVED);
			message.add((int) messageType);
			message.finalize();
			DatagramPacket packet = new DatagramPacket(message.getData(),
					message.getDataLength(), serverSocketAddress);
			clientSocket.send(packet);
			if (VERBOSE) {
				System.out.println("MESSAGE_RECEIVED message sent.");
			}
		} catch (Exception e) {
			System.err.println(e);
		}
	}

	// Not currently working as intended, need to find a different solution
	private void handleMessageReceived(DatagramPacket packet) {
		try {
			if (VERBOSE) {
				System.out
						.println("Received a MESSAGE_RECEIVED message...lul.");
			}
			Message message = new Message(packet);
			if (message.hasNext()) {
				byte messageCode = (byte) message.nextInt();
				if (VERBOSE) {
					System.out.println("messageCode: " + messageCode);
				}
				PacketResponse response = packetResponses.get(messageCode);
				response.setReceived(true);
			}
		} catch (Exception e) {
			System.err.println(e);
		}
	}

	public Lobby getLobby() {
		return lobby;
	}
	
	public void handleError(DatagramPacket packet) {
		errorQueue.offer(new ErrorMessage(packet));
	}

	public boolean isErrorQueueEmpty() {
		return errorQueue.isEmpty();
	}
	
	public ErrorMessage getError() {
		return errorQueue.poll();
	}
	
	public void endClientThread(){
		running = false;
	}
	
	public Game getGame(){
		return game;
	}
}
